package com.guosen.zebra.framework.starter.idm.http.internal;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import com.guosen.zebra.framework.basic.http.OKHttpClientUtil;
import com.guosen.zebra.framework.starter.idm.http.exception.IdmHttpException;
import com.guosen.zebra.framework.starter.idm.http.properties.IdmHttpProperties;
import com.guosen.zebra.framework.starter.idm.http.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static org.apache.commons.lang3.StringUtils.SPACE;

/**
 * IDM http 客户端
 */
@Slf4j
public class IdmHttpClient {

    private static final CharSequence JWT_HEADER_PREFIX = "Bearer";

    private IdmHttpClient() {}

    /**
     * http协议客户端
     */
    private static final OkHttpClient HTTP_CLIENT = OKHttpClientUtil.getHttpClient();

    /**
     * JSON类型
     */
    public static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");
    private static final String CONTENT_TYPE = "Content-Type";
    private static final String APPLICATION_JSON = "application/json";
    private static final String AUTHORIZATION = "Authorization";

    /**
     * https客户端，忽略证书
     */
    private static final OkHttpClient UNSAFE_HTTPS_CLIENT = OKHttpClientUtil.getUnsafeHttpsClient();


    public static JsonNode post(String url, Map<String, String> headers, Object parameters)
            throws IdmHttpException {
        String jsonParam = object2String(parameters);
        Request request = buildRequest(url, headers, jsonParam);
        return doInvoke(request, url);
    }

    private static String object2String(Object object) {
        try {
            return JsonUtil.getObjectMapper().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new IdmHttpException(e);
        }
    }

    private static Request buildRequest(String url, Map<String, String> headers, String jsonParam) {
        try {
            RequestBody body = RequestBody.create(JSON_TYPE, jsonParam);
            Request.Builder builder = new Request.Builder().url(url).post(body);
            setHeaders(headers, builder);
            return builder.build();
        } catch (Exception e) {
            log.error("Failed to resolve url: {}, error message : {}", url, e.getMessage(), e);
            throw new IdmHttpException("请求url解析异常！", e);
        }
    }

    private static void setHeaders(Map<String, String> headers, Request.Builder builder) {
        for (Map.Entry<String, String> header : headers.entrySet()) {
            builder.header(header.getKey(), header.getValue());
        }
    }

    private static JsonNode doInvoke(Request request, String url) throws IdmHttpException {
        JsonNode result = null;
        try (Response response = getHttpClient(url).newCall(request).execute();
             ResponseBody responseBody = response.body()) {
            if (responseBody != null) {
                String respString = responseBody.string();
                if (StringUtils.isNotBlank(respString)) {
                    result = JsonUtil.getObjectMapper().readTree(respString);
                }
            }
        } catch (Exception e) {
            log.error("Failed to invoke {}, error message : {}", url, e.getMessage(), e);
            throw new IdmHttpException("500", e.getMessage());
        }

        return result;
    }

    private static OkHttpClient getHttpClient(String url) {
        boolean isHttps = StringUtils.startsWithIgnoreCase(url, "https");
        return isHttps ? UNSAFE_HTTPS_CLIENT : HTTP_CLIENT;
    }

    public static Map<String, String> getHeaders(IdmHttpProperties idmHttpProperties) {
        Map<String, String> map = new HashMap<>();
        String token = getToken(idmHttpProperties);
        map.put(AUTHORIZATION, token);
        map.put(CONTENT_TYPE, APPLICATION_JSON);
        return map;
    }

    private static String getToken(IdmHttpProperties idmHttpProperties) {
        String token = JWT.create().withIssuer(idmHttpProperties.getAppId())
                .withIssuedAt(new Date())
                .withJWTId(UUID.randomUUID().toString()).sign(Algorithm.HMAC256(idmHttpProperties.getAppSecret()));
        return String.join(SPACE, JWT_HEADER_PREFIX,token);
    }
}
